import { Color, Component, Node, SpriteAtlas, SpriteFrame, Vec2 } from 'cc';
import { MathUtil } from './MathUtil';
import { RollItem } from '../Game/Config/GameEnum';
import { LoadingManager } from '../Basic/Manager/LoadingManager';
/* eslint-disable */

export module Tool {
  /**
   * 递归设置嵌套属性的值
   * @param obj       目标对象
   * @param props     要设置的属性和值
   * @param separator 属性层级分隔符，默认为'.'
   */
  export function setNestedValue(obj: any, props: Record<any, any>, separator = '.'): void {
    for (const key in props) {
      const keys = key.split(separator);
      let i = 0;
      let len = keys.length;
      keys.reduce((acc, k) => {
        i++;
        if (i === len) {
          acc[k] = props[key];
          return;
        }
        if (!acc[k]) return;
        return acc[k];
      }, obj);
    }
  }

  /**
   * 返回数组中第一个属性值的索引，如果不存在，则返回-1
   * @param arr   数组
   * @param prop  要查找的属性 用.分割可以获取任意深度的属性
   * @param value 要查找的值
   * @returns
   */
  export function indexOfByProp<T>(arr: T[], prop: string, value: any): number {
    for (let i = 0; i < arr.length; i++) {
      const temp = getNestedValue(arr[i], prop);
      if (temp === value) {
        return i;
      }
    }
    return -1;
  }
  export async function getSpriteByKey(altasPath: string, key: string) {
    if (altasPath === '' && key.indexOf('/') === -1) return null;
    console.log(altasPath, key);
    if (altasPath != '') {
      const atlas = await LoadingManager.Ins().getLoadRes<SpriteAtlas>(altasPath);
      return atlas.getSpriteFrame(key);
    } else {
      return await LoadingManager.Ins().getLoadRes<SpriteFrame>(key + '/spriteFrame');
    }
  }

  /**
   * 进行真假判定，会按照权重随机返回true或false
   * @param trueProbability 判定为真的可能性，取值范围是[0, 100] ∩ N
   */
  export function judge(trueProbability: number = 50): boolean {
    if (trueProbability >= 100) {
      return true;
    } else if (trueProbability <= 0) {
      return false;
    } else {
      const n = MathUtil.randomBetween(0, 100);
      // console.log('随机概率', n, n < trueProbability);
      return n < trueProbability;
    }
  }

  /**
   * 从指定的数组中按权重抽取元素
   * @param items 定义抽取物品的数组，数组中的每个元素都是RollItem
   * @param num 抽取个数
   * @param allowRepeat 是否允许重复抽取
   * @param padding 物品数量小于抽取数量时是否需要填补
   * @param paddingItem 用于填补的对象，每个空位均会存储这个对象的拷贝，而不是引用
   * @returns 抽取结果
   */
  export function roll(items: RollItem[], num: number = 1, allowRepeat: boolean = false, padding: boolean = true, paddingItem: RollItem = null) {
    const result: RollItem[] = [];
    const remainingItems = [...items];

    if (items.length <= num && !allowRepeat) {
      items.forEach((element) => {
        result.push(element);
      });
      if (padding) {
        for (let i = 0; i < num - items.length; i++) {
          if (paddingItem) {
            // 拷贝指定的对象
            result.push(JSON.parse(JSON.stringify(paddingItem)));
          } else {
            result.push(new RollItem(-1, 1));
          }
        }
      }
    } else {
      remainingItems.sort((a, b) => a.widget - b.widget);
      for (let i = 0; i < num; i++) {
        if (remainingItems.length === 0) break;
        const totalWeight = remainingItems.reduce((acc, item) => acc + item.widget, 0);
        let randomNumber = MathUtil.randomBetween(0, totalWeight);
        for (let j = 0; j < remainingItems.length; j++) {
          if (randomNumber < remainingItems[j].widget) {
            result.push(remainingItems[j]);
            if (!allowRepeat) remainingItems.splice(j, 1);
            break;
          }
          randomNumber -= remainingItems[j].widget;
        }
      }

      // for (let i = 0; i < num; i++) {
      //   result.push(...remainingItems.splice(math.randomRangeInt(0, remainingItems.length), 1));
      // }
    }
    return result;
  }

  /**
   * 打乱一个数组本身
   * @param array 要打乱的数组
   * @returns 这个数组本身的引用
   */
  export function shuffleArray(array: unknown[]) {
    for (let i = array.length - 1; i > 0; i--) {
      const j = MathUtil.randomIntegerBetween(0, i + 1);
      [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
  }

  /** 程序睡眠函数 */
  export function Sleep(ms: number) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  export function getCommonFactor(a: number, b: number) {
    for (let i = 2; i < 100; i++) {
      if (a / i > Math.floor(a / i) || b / i > Math.floor(b / i)) {
        continue;
      } else {
        // console.log(i);
        return Tool.getCommonFactor(a / i, b / i);
      }
    }
    return [a, b];
  }

  export function getNumCount(num: number, index = 1) {
    if (num / Math.pow(10, index) >= 1) {
      return Tool.getNumCount(num, ++index) + 1;
    } else {
      return 1;
    }
  }

  export function getDecimals(num: number) {
    return num - Math.floor(num);
  }

  export function getValueByRange(a: number, b: number, v: number) {
    if ((a > b && v >= a) || (a <= b && v <= a)) {
      return a;
    } else if ((a > b && v <= b) || (a <= b && v >= b)) {
      return b;
    } else {
      return v;
    }
  }

  export function Lerp(a: number, b: number, t: number) {
    return a + (b - a) * t;
  }

  export function LerpVec(v1: Vec2, v2: Vec2, t: number) {
    const v = new Vec2();
    v.x = Tool.Lerp(v1.x, v2.x, t);
    v.y = Tool.Lerp(v1.y, v2.y, t);
    return v;
  }

  export function buttonBindClickHandler(n: Node, c: string, h: string, ...args) {
    const eHandler = new Component.EventHandler();
    eHandler.target = n;
    eHandler.component = c;
    eHandler.handler = h;
    eHandler.customEventData = args.toString();
    return [eHandler];
  }

  export function Vec2Add(v1: Vec2, v2: Vec2) {
    return new Vec2(v1.x + v2.x, v1.y + v2.y);
  }

  export function Vec2Sub(v1: Vec2, v2: Vec2) {
    return new Vec2(v1.x - v2.x, v1.y - v2.y);
  }

  export function Vec2Mut(v: Vec2, s: number) {
    return new Vec2(v.x * s, v.y * s);
  }

  export function Vec2Nor(v: Vec2) {
    if (v.x > v.y) {
      return new Vec2(v.x / v.x, v.y / v.x);
    } else {
      return new Vec2(v.x / v.y, v.y / v.y);
    }
  }

  /**
   * 生成贝塞尔曲线的点
   * @param point1
   * @param point2
   * @param controlPoint1 point1的控制点
   * @param controlPoint2 point2的控制点
   * @param t 取值[0, 1] 设线段point1point2上的某动点p0，t = 线段point1p0长度 / 线段point1point2长度
   * @returns 返回曲线上的某个点
   */
  export function createBezierCurve(startPoint: Vec2, endPoint: Vec2, controlPoint1: Vec2, controlPoint2: Vec2, t: number) {
    const x = Math.pow(1 - t, 3) * startPoint.x + 3 * controlPoint1.x * t * Math.pow(1 - t, 2) + 3 * controlPoint2.x * t * t * (1 - t) + endPoint.x * t * t * t;
    const y = Math.pow(1 - t, 3) * startPoint.y + 3 * controlPoint1.y * t * Math.pow(1 - t, 2) + 3 * controlPoint2.y * t * t * (1 - t) + endPoint.y * t * t * t;
    return new Vec2(x, y);
  }

  /**
   * 贝塞尔曲线控制点计算
   * @param pointsArr 贝塞尔曲线所经过的点
   * @param smooth_value 控制曲线丝滑程度 取值[0, 1]
   * @returns
   */
  export function createBezierCurveControl(pointsArr: Vec2[], smooth_value: number) {
    const points = [...pointsArr];
    points.unshift(new Vec2(points[0].x - 50 * smooth_value, points[0].y + 50 * smooth_value));
    points.push(new Vec2(points[points.length - 1].x + 50 * smooth_value, points[points.length - 1].y + 50 * smooth_value));
    const controlPoints = [];
    const len = points.length - 3;
    for (let i = 0; i < len; i++) {
      const x0 = points[i].x;
      const y0 = points[i].y;
      const x1 = points[i + 1].x;
      const y1 = points[i + 1].y;
      const x2 = points[i + 2].x;
      const y2 = points[i + 2].y;
      const x3 = points[i + 3].x;
      const y3 = points[i + 3].y;
      // 2.求中点
      const xc1 = (x0 + x1) / 2.0;
      const yc1 = (y0 + y1) / 2.0;
      const xc2 = (x1 + x2) / 2.0;
      const yc2 = (y1 + y2) / 2.0;
      const xc3 = (x2 + x3) / 2.0;
      const yc3 = (y2 + y3) / 2.0;

      // 3.求各点连线长度
      const len1 = Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
      const len2 = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
      const len3 = Math.sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2));

      // 4.求连线长度比例（用来确定平移前p2, p3的位置）
      const k1 = len1 / (len1 + len2);
      const k2 = len2 / (len2 + len3);

      // 5.平移点(x1, y1)
      const xm1 = xc1 + (xc2 - xc1) * k1;
      const ym1 = yc1 + (yc2 - yc1) * k1;

      // 6.平移点(x2, y2)
      const xm2 = xc2 + (xc3 - xc2) * k2;
      const ym2 = yc2 + (yc3 - yc2) * k2;

      // 7.微调控制点与顶点之间的距离，越大曲线越平直
      const ctrl1_x = Math.round(xm1 + (xc2 - xm1) * smooth_value + x1 - xm1);
      const ctrl1_y = Math.round(ym1 + (yc2 - ym1) * smooth_value + y1 - ym1);

      const ctrl2_x = Math.round(xm2 + (xc2 - xm2) * smooth_value + x2 - xm2);
      const ctrl2_y = Math.round(ym2 + (yc2 - ym2) * smooth_value + y2 - ym2);

      controlPoints.push(new Vec2(ctrl1_x, ctrl1_y), new Vec2(ctrl2_x, ctrl2_y));
    }
    return controlPoints;
  }

  /**
   * 生成曲线的所有点
   * @param pointsArray
   * @param smoothValue 控制曲线的丝滑程度, 取值[0, 1]
   * @returns 返回曲线的所有点，使用画布绘制即可
   */
  export function createBezierCurvePoints(pointsArray: Vec2[], smoothValue: number) {
    const controlPoints = Tool.createBezierCurveControl(pointsArray, smoothValue);
    const bezierCurvePoints = [];
    for (let i = 0; i < pointsArray.length - 1; i++) {
      for (let j = 0; j <= 100; j++) {
        bezierCurvePoints.push(Tool.createBezierCurve(pointsArray[i], pointsArray[i + 1], controlPoints[2 * i], controlPoints[2 * i + 1], j / 100));
      }
    }
    return bezierCurvePoints;
  }

  /** 旋转平面向量 */
  export function rotateVec2(radius: number, dir: Vec2) {
    const v = new Vec2();
    v.x = dir.x * Math.cos(radius) - dir.y * Math.sin(radius);
    v.y = dir.y * Math.cos(radius) + dir.x * Math.sin(radius);
    return v;
  }

  /** 获取切线高度位置 */
  export function getTangentLinePos(p0: Vec2, p1: Vec2, radius: number, len: number) {
    const centenPos = Tool.Vec2Sub(p1, p0);
    const angle = (Math.atan2(centenPos.y, centenPos.x) * 180) / Math.PI;
    const ret = angle >= 90 || angle <= -90;
    const rot = Tool.Vec2Mut(Tool.rotateVec2(ret ? -radius : radius, centenPos).normalize(), len);
    return Tool.Vec2Add(p0, Tool.Vec2Add(centenPos, rot));
  }

  /** 获取切线高度位置 */
  export function getRandomBezierPos(p0: Vec2, p1: Vec2, radius: number, len: number) {
    const centenPos = Tool.Vec2Sub(p1, p0);
    const tempPos = Tool.Vec2Mut(centenPos.normalize(), Math.random() * centenPos.length());
    const ret = Math.random() > 0.5;
    const rot = Tool.Vec2Mut(Tool.rotateVec2(ret ? -radius : radius, centenPos).normalize(), len);
    return Tool.Vec2Add(p0, Tool.Vec2Add(tempPos, rot));
  }

  /**
   * @desc 获取点，这里可以设置点的个数
   * @param {number} num 点个数
   * @param {Array} p1 点坐标
   * @param {Array} p2 点坐标
   * @param {Array} p3 点坐标
   * @param {Array} p4 点坐标
   * 如果参数是 num, p1, p2 为一阶贝塞尔
   * 如果参数是 num, p1, c1, p2 为二阶贝塞尔
   * 如果参数是 num, p1, c1, c2, p2 为三阶贝塞尔
   */
  export function getBezierPoints(num = 100, p1: Vec2, p2: Vec2, p3: Vec2[], p4: Vec2[]) {
    let func = null;
    const points = [];
    if (!p3 && !p4) {
      func = Tool.oneBezier;
    } else if (p3 && !p4) {
      func = Tool.twoBezier;
    } else if (p3 && p4) {
      func = Tool.threeBezier;
    }
    for (let i = 0; i < num; i++) {
      points.push(func(i / num, p1, p2, p3, p4));
    }
    if (p4) {
      points.push(...p4);
    } else if (p3) {
      points.push(...p3);
    }
    return points;
  }

  /**
   * @desc 一阶贝塞尔
   * @param {number} t 当前百分比
   * @param {Array} p1 起点坐标
   * @param {Array} p2 终点坐标
   */
  export function oneBezier(t: number, p1: Vec2, p2: Vec2) {
    const v = new Vec2();
    v.x = p1.x + (p2.x - p1.x) * t;
    v.y = p1.y + (p2.y - p1.y) * t;
    return v;
  }

  /**
   * @desc 二阶贝塞尔
   * @param {number} t 当前百分比
   * @param {Array} p1 起点坐标
   * @param {Array} p2 终点坐标
   * @param {Array} cp 控制点
   */
  export function twoBezier(t: number, p1: Vec2, cp: Vec2, p2: Vec2) {
    const v = new Vec2();
    v.x = (1 - t) * (1 - t) * p1.x + 2 * t * (1 - t) * cp.x + t * t * p2.x;
    v.y = (1 - t) * (1 - t) * p1.y + 2 * t * (1 - t) * cp.y + t * t * p2.y;
    return v;
  }

  /**
   * @desc 三阶贝塞尔
   * @param {number} t 当前百分比
   * @param {Array} p1 起点坐标
   * @param {Array} p2 终点坐标
   * @param {Array} cp1 控制点1
   * @param {Array} cp2 控制点2
   */
  export function threeBezier(t: number, p1: Vec2, cp1: Vec2, cp2: Vec2, p2: Vec2) {
    // const [x1, y1] = p1;
    // const [x2, y2] = p2;
    // const [cx1, cy1] = cp1;
    // const [cx2, cy2] = cp2;
    const v = new Vec2();
    v.x = p1.x * (1 - t) * (1 - t) * (1 - t) + 3 * cp1.x * t * (1 - t) * (1 - t) + 3 * cp2.x * t * t * (1 - t) + p2.x * t * t * t;
    v.y = p1.y * (1 - t) * (1 - t) * (1 - t) + 3 * cp1.y * t * (1 - t) * (1 - t) + 3 * cp2.y * t * t * (1 - t) + p2.y * t * t * t;
    return v;
  }

  // anchorpoints：贝塞尔基点
  // pointsAmount：生成的点数
  // return 路径点的Array
  export function CreateBezierPoints(anchorpoints: Vec2[], pointsAmount: number) {
    const points = [];
    for (let i = 0; i < pointsAmount; i++) {
      const point = Tool.MultiPointBezier(anchorpoints, i / pointsAmount);
      points.push(point);
    }
    return points;
  }

  export function MultiPointBezier(points: Vec2[], t: number) {
    const len = points.length;
    let x = 0;
    let y = 0;
    const erxiangshi = function (start, end) {
      let cs = 1;
      let bcs = 1;
      while (end > 0) {
        cs *= start;
        bcs *= end;
        start--;
        end--;
      }
      return cs / bcs;
    };
    for (let i = 0; i < len; i++) {
      const point = points[i];
      x += point.x * Math.pow(1 - t, len - 1 - i) * Math.pow(t, i) * erxiangshi(len - 1, i);
      y += point.y * Math.pow(1 - t, len - 1 - i) * Math.pow(t, i) * erxiangshi(len - 1, i);
    }
    return new Vec2(x, y);
  }

  export function MatchCode(str: string, num: number) {
    return String.fromCharCode(str.charCodeAt(0) + num);
  }

  export function CryptographicString(str: string, value = 0) {
    let tempStr = '';
    for (let i = 0; i < str.length; i++) {
      tempStr += Tool.MatchCode(str[i], value);
    }
    return tempStr;
  }

  export function DecryptionString(str: string, value = -0) {
    let tempStr = '';
    for (let i = 0; i < str.length; i++) {
      tempStr += Tool.MatchCode(str[i], value);
    }
    return tempStr;
  }

  export function colorToGrayScale(color: Color) {
    if (!color) return;
    const gray = color.r * 0.3 + color.g * 0.59 + color.b * 0.11;
    return new Color(gray, gray, gray);
  }

  /** 跟据数据的属性，对数据进行排序 */
  export function sortArray(arr: any[], key: any[], sort?: number[]): any[] {
    if (!sort) {
      // 默认从小到大排序
      sort = new Array(key.length).fill(1);
    }

    // 一次性完成所有键的排序
    arr.sort((a, b) => {
      for (let i = 0; i < key.length; i++) {
        const comparison = a[key[i]] - b[key[i]];
        if (comparison !== 0) {
          return comparison * sort[i];
        }
      }
      return 0;
    });

    return arr;
  }

  // 递归获取嵌套属性的值
  export function getNestedValue(obj: any, key, separator = '.'): any {
    const keys = key.split(separator);
    return keys.reduce((acc, key) => acc && acc[key], obj);
  }

  /** 跟据数据的属性，可以指定任意深度的属性，对数据进行排序 */
  export function sortArraySuper(arr: any[], key: string[], sort?: number[]): any[] {
    if (!sort) {
      // 默认从小到大排序
      sort = new Array(key.length).fill(1);
    }

    // 一次性完成所有键的排序
    arr.sort((a, b) => {
      for (let i = 0; i < key.length; i++) {
        const aValue = Tool.getNestedValue(a, key[i], '.') ?? 0;
        const bValue = Tool.getNestedValue(b, key[i], '.') ?? 0;
        const comparison = aValue - bValue;
        if (comparison !== 0) {
          return comparison * sort[i];
        }
      }
      return 0;
    });

    return arr;
  }

  export function hasSameValue(arr1: number[], arr2: number[]): boolean {
    for (const value of arr1) {
      if (arr2.includes(value)) {
        return true;
      }
    }
    return false;
  }
}
